package com.ticket.sass.provider.aspect;

import com.google.gson.Gson;
import com.ticket.sass.common.exception.exception.BaseException;
import com.ticket.sass.provider.util.GsonUtil;
import com.ticket.sass.provider.util.JwtUtil;
import com.ticket.sass.provider.util.ResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.IntStream;


/**
 * web 日志记录
 * @author ywb
 * @date 2022-02-15 15:59
 */
@Aspect
@Slf4j
@Component
public class WebLogAspect {
    
    final Gson gson = GsonUtil.getInstance();
    
    @Pointcut("execution(public * com.ticket.sass.provider.controller.*.*(..))")
    public void webLogAspect() {
    }
    
    @Around("webLogAspect()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable{
        JwtUtil jwtUtil = new JwtUtil();
        
        HttpServletRequest request = ResponseUtil.getRequest();
        String account = jwtUtil.getSubBy(request);
        String url = request.getRequestURL().toString();
        String ip = ResponseUtil.getRemoteHost(request);
        Map<String, Object> requestParamMap = preHandle(joinPoint);
        String requestParam = gson.toJson(requestParamMap);
        
        String method = request.getMethod();
        long start = System.currentTimeMillis();
        long end = System.currentTimeMillis();
        Object result;
        try {
            // 调用下一个切面或目标方法
            result = joinPoint.proceed();
            end = System.currentTimeMillis();
            String responseResult = postHandle(result);
            //记录日志(用户行为信息)
            printLog(account, url, ip, method, requestParam, responseResult, (end - start));
        } catch (BaseException baseException){
            printLog(account, url, ip, method, requestParam, baseException.getMessage(), (end - start));
            throw baseException;
        }
        return result;
    }

    /**
     * 日志打印
     * @param account 用户名
     * @param url 路径
     * @param ip IP
     * @param method 方法类型
     * @param requestParam 请求参数
     * @param responseResult 响应结果
     * @param time 响应时间
     */
    private void printLog(
        String account,
        String url,
        String ip,
        String method,
        String requestParam,
        String responseResult,
        long time
    ) {
        // 所有操作请求的记录，记录基本字段：登录用户名，请求地址，请求参数，请求时间，响应结果
        log.info(
            "登录账号【{}】, 请求地址【{}】, 请求IP【{}】, 请求方式【{}】，请求参数【{}】, 响应结果【{}】, 响应时间【{}】",
            account,
            url,
            ip,
            method,
            requestParam,
            responseResult,
            time
        );
    }

    /**
     * 解析返回数据
     */
    private String postHandle(Object retVal) {
        if(Objects.isNull(retVal)){
            return "";
        }
        
        return gson.toJson(retVal);
    }

    /**
     * 解析入参数据
     */
    private Map<String, Object> preHandle(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        
        return IntStream.range(0, args.length)
            .boxed()
            .collect(Collectors.toMap(i -> argNames[i], i -> args[i]));
    }
}